/**
 * Copyright © FEITIAN Technologies Co., Ltd. All Rights Reserved.
 * https://www.ftsafe.com/
 */
#include <string.h>
#include "fs_port.h"


#include "MercuryNv.h"



//外部flash p25q64 

#define FILE_SYSTME_SIZE         500   //16k 16k/4096

/* 文件系统起始地址<需4K对齐> */
#define FILE_SYSTEM_START        (0)//(0x08000000 + 0x7C800)   //取最后的16k 总共512k  flash的地址范围是0x08000000~0x0807E7FF
/* Flash 操作超时时间 */
#define FILE_SYSTEM_TIMEOUT      (20 * 1000)

static lfs_t lfs;

#ifdef LFS_THREADSAFE
static feitian_mutex_t lfs_mutex;
// Lock the underlying block device. Negative error codes
// are propogated to the user.
int _lfs_lock(const struct lfs_config *c)
{
    if (feitian_mutex_lock((feitian_mutex_t *)c->context, 60 * 1000) != FEITIAN_OS_OK)
    {
          MercuryTrace("%s lock error!\r\n", __FUNCTION__);
        return -1;
    }

    // feitian_log(LOG_DEBUG, "%s lock unccess!\r\n", __FUNCTION__);
    return 0;
}

// Unlock the underlying block device. Negative error codes
// are propogated to the user.
int _lfs_unlock(const struct lfs_config *c)
{
    if (feitian_mutex_unlock((feitian_mutex_t *)c->context) != FEITIAN_OS_OK)
    {
          MercuryTrace("%s unlock error!\r\n", __FUNCTION__);
        return -1;
    }
    // feitian_log(LOG_DEBUG, "%s unlock success!\r\n", __FUNCTION__);
    return 0;
}
#endif

static int _block_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size)
{   
    char temp[1024] = "";
    int ret = 0;
    MercuryTrace("_block_read %x %d %d\n",block * c->block_size + off + FILE_SYSTEM_START,size,ret);
    Sleep(200);
    ret = NV_Read(block, sizeof(temp),temp);
    // if(0 != ret)
    // {   
    //     MercuryTrace("_block_read ret = %d\r\n", ret);
    //     return LFS_ERR_IO;
    // }
    memcpy(buffer,temp+off,size);
    //NorFlash_Read(buffer,block * c->block_size + off + FILE_SYSTEM_START, size);
   
    // ft_LITE_hexdump(buffer,size);
    return LFS_ERR_OK;
}
static int _block_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size)
{   
    char temp[1024] = "";
    int ret = 0;
    MercuryTrace("_block_prog %x %d %d\n",block * c->block_size + off + FILE_SYSTEM_START,size,ret);
    Sleep(200);
    ret = NV_Read(block, sizeof(temp),temp);
    // if(0 != ret)
    // {   
    //       MercuryTrace("_block_prog ret = %d\r\n", ret);
    //     return LFS_ERR_IO;
    // }
    memcpy(temp+off,buffer,size);
    //memcpy(buffer,temp,off+size);
    if (0 != NV_Write(block, sizeof(temp), temp)) {
        NV_Delete(block);
        NV_Write(block,sizeof(temp), temp);
    } 
    
    //W25QXX_STD_Write_NoCheck(buffer,block * c->block_size + off + FILE_SYSTEM_START,size);
    
    //ft_LITE_hexdump(buffer,size);
    return LFS_ERR_OK;
}
static int _block_erase(const struct lfs_config *c, lfs_block_t block)
{
    int ret = 0;
    NV_Delete(block);
    //W25QXX_SectorErase4K(block * c->block_size + FILE_SYSTEM_START);
    //  MercuryTrace("_block_erase %x %d\n",block * c->block_size  + FILE_SYSTEM_START,ret);

    return LFS_ERR_OK;
}
static int _block_sync(const struct lfs_config *c)
{
    return 0;
}



// configuration of the filesystem is provided by this struct
const struct lfs_config cfg = {
    // block device operations
#ifdef LFS_THREADSAFE
    .context = (void *)&lfs_mutex,
#endif
    .read  = _block_read,
    .prog  = _block_prog,
    .erase = _block_erase,
    .sync  = _block_sync,

    // block device configuration
    .read_size = 1024, /* 页大小 */
    .prog_size = 1024, /* 页大小 */
    .block_size = 1024, /* 块大小 */
    .block_count = FILE_SYSTME_SIZE, /* 总共多少个块<文件系统管理多少> */
    .cache_size = 1024,      /* 缓存大小 */
    .lookahead_size = 512,
    /* lookahead_size 按照如下计算处理，必须是8的倍数*/
    // #define LFS_LOOKAHEAD_MAX 128
    // lfs_cfg->lookahead_size = 32 * ((lfs_cfg->block_count + 31) / 32);
    // if (lfs_cfg->lookahead_size > LFS_LOOKAHEAD_MAX)
    // {
    //     lfs_cfg->lookahead_size = LFS_LOOKAHEAD_MAX;
    // }
    .block_cycles = -1, /* -1表示禁用块级磨损平衡 */
    /* 线程安全锁的实现 */
#ifdef LFS_THREADSAFE
    .lock = _lfs_lock,
    .unlock = _lfs_unlock,
#endif
};


/**
 * @brief  文件系统初始化
 * @note   
 * @retval None
 */
void feitian_fs_init(void)
{
    /* 创建文件系统锁 */
#ifdef LFS_THREADSAFE
    if (FEITIAN_OS_OK != feitian_mutex_create(&lfs_mutex))
    {
          MercuryTrace("## feitian file system mutex create error!");
    }
#endif
    NV_Init();
    int err = lfs_mount(&lfs, &cfg);

    // reformat if we can't mount the filesystem
    // this should only happen on the first boot
      MercuryTrace("## feitian file system mount %d\r\n",err);
    if (err) {
        feitian_fs_format();
    };
}


/**
 * @brief  文件系统格式化
 * @note   
 * @retval None
 */
void feitian_fs_format(void)
{
    int err = 0;
    err = lfs_format(&lfs, &cfg);
      MercuryTrace("## feitian file system re-format %d\r\n",err);
    err = lfs_mount(&lfs, &cfg);
      MercuryTrace("## feitian file system remount mount %d\r\n",err);
}

/**
 * @brief  获取文件系统剩余空间
 * @note   
 * @retval None
 */
void feitian_get_fs_size(void)
{
    int size = 0;

    size = lfs_fs_size(&lfs);

      MercuryTrace("feitian fs use size:%d / %d Bytes\r\n", size * 4096, FILE_SYSTME_SIZE * 4096);
}

/**
 * @brief  目录创建
 * @note   
 * @param  *path: 
 * @retval 
 */
int32_t feitian_fs_mkdir(const char *path)
{
    return lfs_mkdir(&lfs, path);
}

#if 0
/**
 * @brief  
 * @note   
 * @param  *path: 
 * @param  *dst: 
 * @param  size: 
 * @retval 
 */
int32_t feitian_read_file_(const char *path, void *dst, uint32_t size)
{
    lfs_file_t file;
    int32_t read_len = 0;
    int ret = 0;

    ret = lfs_file_open(&lfs, &file, path, LFS_O_RDONLY);
    if (ret < 0)
    {
          MercuryTrace("read open file error![ret:%d]%s\r\n", ret, path);
        return -1;
    }

    memset(dst, 0, size);
    read_len = lfs_file_read(&lfs, &file, dst, size);
    lfs_file_close(&lfs,&file);
    return read_len;
}

/**
 * @brief  
 * @note   
 * @param  *path: 
 * @param  *dst: 
 * @param  size: 
 * @retval 
 */
int32_t feitian_write_file_(const char *path, void *dst, uint32_t size)
{
    lfs_file_t file;
    int ret = 0;
    int32_t write_len = 0;

    lfs_remove(&lfs, path);;
    ret = lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
    if (ret < 0)
    {
          MercuryTrace("write open file error![ret:%d] %s\r\n", ret, path);
        return -1;
    }

    write_len = lfs_file_write(&lfs, &file, dst, size);
    lfs_file_close(&lfs,&file);
    return write_len;
}
#endif

/**
 * @brief  文件打开
 * @note   
 * @param  *pathname: 
 * @param  opt: 
 * @retval 
 */
feitian_fs_t feitian_fs_file_open(const uint8_t *pathname, uint32_t opt)
{
    lfs_file_t *file = NULL;

    file = FT_Malloc(sizeof(lfs_file_t));
    if( NULL == file)
    {
          MercuryTrace("feitian fs open mallo error!\r\n");
        return (feitian_fs_t)-1;
    }

    memset(file, 0, sizeof(lfs_file_t));

    if(lfs_file_open(&lfs, file, (const char *)pathname, opt) < 0)
    {
        FT_Free(file);
        return (feitian_fs_t) -1;
    }

    return (feitian_fs_t)file;
}

/**
 * @brief  读文件
 * @note   
 * @param  fd: 
 * @param  *buff: 
 * @param  size: 
 * @retval 
 */
int32_t feitian_fs_file_read(feitian_fs_t fd, uint8_t *buff, uint32_t size)
{
    if(fd == -1 || NULL == buff)
    {
          MercuryTrace("%s paramer error!\r\n", __FUNCTION__);
        return -1;
    }
    return (int32_t)lfs_file_read(&lfs, (lfs_file_t *)fd, buff, size);
}

/**
 * @brief  写文件
 * @note   
 * @param  fd: 
 * @param  *buff: 
 * @param  size: 
 * @retval 
 */
int32_t feitian_fs_file_write(feitian_fs_t fd, uint8_t *buff, uint32_t size)
{
    if(fd == -1 || NULL == buff)
    {
          MercuryTrace("%s paramer error!\r\n", __FUNCTION__);
        return -1;
    }

    return (int32_t)lfs_file_write(&lfs, (lfs_file_t *)fd, buff, size);
}

/**
 * @brief  查找文件
 * @note   
 * @param  fd: 
 * @param  offset: 
 * @param  opt: 
 * @retval 
 */
int32_t feitian_fs_file_seek(feitian_fs_t fd, uint32_t offset, uint32_t opt)
{
    if(fd == -1)
    {
        MercuryTrace("%s paramer error!\r\n", __FUNCTION__);
        return -1;
    }
    return (int32_t)lfs_file_seek(&lfs, (lfs_file_t *)fd, offset, opt);
}

/**
 * @brief  关闭文件
 * @note   
 * @param  fd: 
 * @retval 
 */
int32_t feitian_fs_file_close(feitian_fs_t fd)
{
    int ret = - 1;

    if(fd != -1)
    {
        ret = lfs_file_close(&lfs,(lfs_file_t *)fd);

        if( NULL != (void *)fd)
        {
            FT_Malloc((void *)fd);
        }
    }
    return ret;
}

/**
 * @brief  删除文件
 * @note   
 * @param  *path: 
 * @retval None
 */
void feitian_fs_file_delete(const char *path)
{
    int err = 0;

    err = lfs_remove(&lfs, path);
       MercuryTrace( "%s delete file %s, %d\r\n", __FUNCTION__ , path, err);
}

/**
 * @brief  获取文件大小
 * @note   
 * @param  fd: 
 * @retval 
 */
int32_t feitian_fs_file_size(feitian_fs_t fd)
{
    if(fd == -1)
    {
          MercuryTrace("%s paramer error!\r\n", __FUNCTION__);
        return -1;
    }
    return (int32_t)lfs_file_size(&lfs, (lfs_file_t *)fd);
}

/**
 * @brief  文件重命名
 * @note   
 * @param  *oldpath: 
 * @param  *newpath: 
 * @retval 
 */
int32_t feitian_fs_file_rename(const char *oldpath, const char *newpath)
{
    if(oldpath == NULL || newpath == NULL)
    {
          MercuryTrace("%s paramer error!\r\n", __FUNCTION__);
        return -1;
    }

    return lfs_rename(&lfs, oldpath, newpath);
}



